/*!
 * dynamiccss.js by Joern Heid (heid(youknowwhat)agentsinaction.de
 * Version 01.04.2009
 * Apache 2.0 License
 */
dynamicCSS = (function () {
 // From ExtJS
 var isIE = !new RegExp ('opera').test (navigator.userAgent) && new RegExp ('msie').test (navigator.userAgent), cachedCSSFiles = {};

 var leftPad = function (text, length, subst) {
  var i;
  for (i = text.length; i < length; i++)
      text = subst+text;
  return text;
  };
 var removeStyleSheet = function (id) {
  var existing = document.getElementById(id);
  if (existing){
     existing.parentNode.removeChild(existing);
     }
  };
 var swapStyleSheet = function (id, url) {
  removeStyleSheet (id);
  var ss = document.createElement ("link");
  ss.setAttribute ("rel", "stylesheet");
  ss.setAttribute ("type", "text/css");
  ss.setAttribute ("id", id);
  ss.setAttribute ("href", url);
  document.getElementsByTagName("head")[0].appendChild(ss);
  };
 var createStyleSheet = function (cssText, id) {
  var ss, head = document.getElementsByTagName("head")[0], rules = document.createElement("style");
   rules.setAttribute("type", "text/css");
   if (id) rules.setAttribute ("id", id);
   if (isIE){
      head.appendChild(rules);
      ss = rules.styleSheet;
      ss.cssText = cssText;
      } else {
             try {
                 rules.appendChild(document.createTextNode(cssText));
                 }catch (e) {
                        rules.cssText = cssText;
                        }
             head.appendChild (rules);
             ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || document.styleSheets[document.styleSheets.length-1]);
             }
   return ss;
   };

 // Determines if the colors are the same except for brightness
 var getColorDiff = function (color1, color2) {
  var diffR = color2[0] - color1[0], 
      diffG = color2[1] - color1[1], 
      diffB = color2[2] - color1[2],
      diff = color1[0] != 255 && color1[0] != 0 && color2[0] != 255 && color2[0] != 0 ? diffR :
            color1[1] != 255 && color1[1] != 0 && color2[1] != 255 && color2[1] != 0 ? diffG :
            color1[2] != 255 && color1[2] != 0 && color2[2] != 255 && color2[2] != 0 ? diffB : undefined;
  return diff && (((diff < 0 ? color1[0] == 255 || color2[0] == 0 : color1[0] == 0 || color2[0] == 255) || (color1[0] + diff > 255 || color1[0] + diff < 0 || color1[0] + diff == color2[0])) &&
         ((diff < 0 ? color1[1] == 255 || color2[1] == 0 : color1[1] == 0 || color2[1] == 255) || (color2[1] + diff > 255 || color1[1] + diff < 0 || color1[1] + diff == color2[1])) &&
         ((diff < 0 ? color1[2] == 255 || color2[2] == 0 : color1[2] == 0 || color2[2] == 255) || (color2[2] + diff > 255 || color1[2] + diff < 0 || color1[2] + diff == color2[2]))) ? diff : undefined;
  };
 
 //console.log (getColorDiff ([160,0,0], [255,0,0]));


 // Tries to load an image. Calls the callbacks wether this was successfull or not
 var loadImage = function (src, successCallback, errorCallback) {
  var image = new Image ();
  image.onload = function () { successCallback (image); };
  image.onerror = errorCallback;
  image.src = src;
  };

 // Generates data uri if X-UNDERLAY.gif and X-OVERLAY.png present
 var generateDataURI = function (baseURI, orgColor, newColor, callback) {
  loadImage (baseURI+'-UNDERLAY.gif', function (image) {
   // UNDERLAY existing - generate combined image with canvas
   var canvas = document.createElement ('canvas');
   canvas.style.position = 'absolute';
   canvas.style.top = '-5000px';
   document.body.appendChild (canvas);
   var context = canvas.getContext ('2d');
   var w = image.width;
   var h = image.height;
   canvas.width = w;
   canvas.height = h;
   context.drawImage (image, 0, 0);
   var input = context.getImageData (0, 0, w, h);
   var output = context.createImageData (w, h);
   var inputData = input.data;
   var outputData = output.data;
   var r,g,b,a, start,
       newColorR = parseInt (newColor.substring (0, 2), 16),
       newColorG = parseInt (newColor.substring (2, 4), 16),
       newColorB = parseInt (newColor.substring (4, 6), 16),
       orgColorR = parseInt (orgColor.substring (0, 2), 16),
       orgColorG = parseInt (orgColor.substring (2, 4), 16),
       orgColorB = parseInt (orgColor.substring (4, 6), 16),
       sameColor;
   for (var y = 0; y < h; y++)
       for (var x = 0; x < w; x++) {
           start = 4*(y*w+x);
           r = inputData[start+0];
           g = inputData[start+1];
           b = inputData[start+2];
           a = inputData[start+3];
           sameColor = r === orgColorR && g === orgColorG && b === orgColorB;
           outputData[start+0] = sameColor ? newColorR : r;
           outputData[start+1] = sameColor ? newColorG : g;
           outputData[start+2] = sameColor ? newColorB : b;
           outputData[start+3] = a;
           }
   context.putImageData (output, 0, 0);
   // Now put the overlay on it
   loadImage (baseURI+'-OVERLAY.png', function (image) {
    context.drawImage (image, 0, 0);
    var d = canvas.toDataURL ('image/png');
    document.body.removeChild (canvas);
    callback (d);
    });
   }, function () { callback (false); });
  };


 // Replaces all the the color
 var replaceColor = function (conf) {
  var css = cachedCSSFiles[conf.cssURI], 
      re = new RegExp ('url\\s*\\(('+conf.newBaseURI.replace (/\./g, '\\.')+'[^\\)]+).(gif|png)\\s*\\)', 'g'),
      m,
      hits = [],
      counter = 0, 
      newColor = [ 
       parseInt (conf.newColor.substring (0, 2), 16),
       parseInt (conf.newColor.substring (2, 4), 16),
       parseInt (conf.newColor.substring (4, 6), 16),
       255
       ],
      colorRE = /#([a-zA-z0-9]{3,6})/g,
      orgRed = parseInt (conf.orgColor.substring (0, 2), 16),
      orgGreen = parseInt (conf.orgColor.substring (2, 4), 16),
      orgBlue = parseInt (conf.orgColor.substring (4, 6), 16),
      r, g, b, diff;
  // Change all color like the original one
  while ((m = colorRE.exec (css))) {
        r = parseInt (m[1].length === 3 ? m[1].substring (0, 1)+m[1].substring (0, 1) : m[1].substring (0, 2), 16);
        g = parseInt (m[1].length === 3 ? m[1].substring (1, 2)+m[1].substring (1, 2) : m[1].substring (2, 4), 16);
        b = parseInt (m[1].length === 3 ? m[1].substring (2, 3)+m[1].substring (2, 3) : m[1].substring (4, 6), 16);
        // Is this just a darker or brighter version of the original color?
        diff = getColorDiff ([ r, g, b], [ orgRed, orgBlue, orgGreen ]);
        //console.log (m[1]+' '+diff+' '+[ r, g, b]+' '+[ orgRed, orgBlue, orgGreen ]);
        if (diff) {
	   var newValue = leftPad (Math.min (255, Math.max (0, newColor[0]-diff)).toString (16), 2, '0')+
                          leftPad (Math.min (255, Math.max (0, newColor[1]-diff)).toString (16), 2, '0')+
                          leftPad (Math.min (255, Math.max (0, newColor[2]-diff)).toString (16), 2, '0');
           css = css.replace (m[1], newValue);
	   }
        }

  var changes = [];
  var changeCSS = function (url, data) {
   if (data)
      changes.push ([ url, 'url(\''+data+'\')' ]);
   if (--counter == 0) {
      for (var i = 0; i < changes.length; i++) {
          css = css.replace (changes[i][0], changes[i][1]);
          } 
      // Set stylesheet
      removeStyleSheet (conf.cssID);
      createStyleSheet (css, conf.cssID);
      // Store css if possible
      if (window.localStorage) window.localStorage.dynamicCSSStyleSheet = css;
      (conf.callback || function () {}) ();
      }
   };

  while ((m = re.exec (css))) {
        ++counter;
        (function (ma) {
         generateDataURI (ma[1], conf.orgColor, conf.newColor, function (data) {
          changeCSS (ma[0], data);
          });
         }) (m);
        }   
  };

 // Checks the config. Can be emoved for smaller size
 var checkConfig = function (conf) {  
  var canvasSupport = false;
  try {
      canvasSupport = !!(document.createElement ('canvas').getContext ('2d'));
      } catch(e) { canvasSupport = !!(document.createElement ('canvas').getContext); } 
  if (!canvasSupport) throw 'Your browser does not support canvas';
  var checkProp = function (names) {
   var i; 
   for (i = 0; i < names.length; i++)
       if (!conf[names[i]]) throw 'Missing conf.'+name[i];
   };
  checkProp (conf, [ 'cssURI', 'orgColor', 'newColor', 'orgBaseURI', 'newBaseURI', 'cssID' ]);
  };
   
 return {

  /**
   * Replace color in stylesheet (rgb values and generated images)
   * @param {Object} conf  The configuration object with
   *  <ul>
   *   <li><b>cssURI</b>: The uri to the CS file</li>
   *   <li><b>orgColor</b>: The original color (image or rgb) for replacement (hex)</li>
   *   <li><b>newColor</b>: The new color for replacement (hex)</li>
   *   <li><b>orgBaseURI</b>: The uri subpath which points to the images (relative to the CSS)</li>
   *   <li><b>newBaseURI</b>: The uri subpath whch points to the images now (relative to the document)</li>
   *   <li><b>cssID</b>: The id of the CSS-tag which will be replaced</li>
   *  </ul>
   */
  replaceColor: function (conf) {
   checkConfig (conf);
   if (conf.orgColor.indexOf ('#') == 0) conf.orgColor = conf.orgColor.substring (1);
   if (conf.newColor.indexOf ('#') == 0) conf.newColor = conf.newColor.substring (1);
   if (!cachedCSSFiles[conf.cssURI]) {
      var ajax = new XMLHttpRequest ();
      ajax.open ('GET', conf.cssURI, true);
      ajax.onreadystatechange = function () {
       if (ajax.readyState === 4) {
          var css = ajax.responseText;
          // Replace oldBaseURI with new newBaseURI
          css = css.replace (new RegExp (conf.orgBaseURI.replace (/\./g, '\\.'), 'g'), conf.newBaseURI);
          cachedCSSFiles[conf.cssURI] = css;          
          replaceColor (conf);
          }          
       }; 
      ajax.send (null);
      } else replaceColor (conf);
   },


  /**
   * Call this function, if a previous generated CSS should be loaded. 
   * @param {String} cssID       The ID of the style tag
   * @param {String} defaultCSS  The URL to the CSS used if no stored generated CSS found
   */
  initStylesheet: function (cssID, defaultCSS) {
   //window.localStorage.clear ();
   if (window.localStorage && window.localStorage.dynamicCSSStyleSheet) {
      removeStyleSheet (cssID);
      createStyleSheet (window.localStorage.dynamicCSSStyleSheet, cssID);
      } else {
             swapStyleSheet (cssID, defaultCSS);
             }
   }
  };
 }) ();